package cn.ug.pay.service.impl;

import cn.ug.analyse.bean.request.MemberAnalyseMsgParamBean;
import cn.ug.bean.LoginBean;
import cn.ug.bean.base.DataTable;
import cn.ug.bean.base.SerializeObject;
import cn.ug.config.RedisGlobalLock;
import cn.ug.core.ensure.Ensure;
import cn.ug.core.login.LoginHelper;
import cn.ug.enums.*;
import cn.ug.feign.RateSettingsService;
import cn.ug.mall.bean.GoldBean;
import cn.ug.mq.DelayMessagePostProcessor;
import cn.ug.msg.bean.status.CommonConstants;
import cn.ug.msg.mq.Msg;
import cn.ug.msg.mq.WxMessageParamBean;
import cn.ug.msg.mq.WxNotifyData;
import cn.ug.pay.bean.AreaBalanceBean;
import cn.ug.pay.bean.AreaTransactionBean;
import cn.ug.pay.bean.BillBean;
import cn.ug.pay.bean.MemberGoldBean;
import cn.ug.pay.bean.request.MemberAccountParamBean;
import cn.ug.pay.bean.response.BankCardManageBean;
import cn.ug.pay.bean.response.MemberAccountBean;
import cn.ug.pay.bean.response.MemberAccountManageBean;
import cn.ug.pay.bean.status.PayDistributePrefix;
import cn.ug.pay.bean.type.BillType;
import cn.ug.pay.bean.type.TradeType;
import cn.ug.pay.component.PayCommon;
import cn.ug.pay.mapper.*;
import cn.ug.pay.mapper.entity.*;
import cn.ug.pay.service.BillService;
import cn.ug.pay.service.MemberAccountService;
import cn.ug.pay.service.MemberGoldService;
import cn.ug.util.*;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageHelper;
import org.apache.commons.lang3.StringUtils;
import org.dozer.DozerBeanMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static cn.ug.config.QueueName.*;
import static cn.ug.config.QueueName.QUEUE_MEMBER_ANALYSE;

@Service
public class MemberAccountServiceImpl implements MemberAccountService {

    @Resource
    private MemberAccountMapper memberAccountMapper;

    @Resource
    private MemberGoldService memberGoldService;

    @Resource
    private BillService billService;

    @Resource
    private RedisGlobalLock redisGlobalLock;

    @Resource
    private PayCommon payCommon;

    @Resource
    private TradeMapper tradeMapper;

    @Resource
    private DozerBeanMapper dozerBeanMapper;

    @Resource
    private AmqpTemplate amqpTemplate;

    @Resource
    private ExperienceBillMapper experienceBillMapper;
    @Resource
    private RateSettingsService rateSettingsService;

    @Resource
    private PayGoldBeanRecordMapper payGoldBeanRecordMapper;

    @Resource
    private BankCardMapper bankCardMapper;

    private static Logger logger = LoggerFactory.getLogger(MemberAccountServiceImpl.class);

    @Override
    public List<AreaBalanceBean> listAreaBalance(String order, String sort) {
        Map<String, Object> params = new HashMap<String, Object>();
        if (StringUtils.isNotBlank(order) && (StringUtils.equals(order, "peopleNum") || StringUtils.equals(order, "balance"))) {
            params.put("order", order);
            if (StringUtils.isNotBlank(sort) && (StringUtils.equalsIgnoreCase(sort, "DESC") || StringUtils.equalsIgnoreCase(sort, "ASC"))) {
                params.put("sort", sort);
            } else {
                params.put("sort", "DESC");
            }
        }
        List<AreaBalanceBean> result = memberAccountMapper.selectAreaBalance(params);
        if (result != null && result.size() > 0) {
            for (AreaBalanceBean bean : result) {
                if (bean.getBalance() == null) {
                    bean.setBalance(BigDecimal.ZERO);
                }
            }
        }
        return result;
    }

    @Override
    public List<AreaTransactionBean> listAreaTransaction() {
        List<AreaTransactionBean> result = memberAccountMapper.selectAreaTransaction();
        if (result != null && result.size() > 0) {
            int totalUsers = 0;
            for (AreaTransactionBean bean : result) {
                totalUsers += bean.getPeopleNum();
                if (bean.getBalance() == null) {
                    bean.setBalance(BigDecimal.ZERO);
                }
                if (bean.getTotalCurrentAmount() == null) {
                    bean.setTotalCurrentAmount(BigDecimal.ZERO);
                }
                if (bean.getTotalMoney() == null) {
                    bean.setTotalMoney(BigDecimal.ZERO);
                }
                if (bean.getTotalRegularlyAmount() == null) {
                    bean.setTotalRegularlyAmount(BigDecimal.ZERO);
                }
            }
            for (AreaTransactionBean bean : result) {
                if (totalUsers > 0) {
                    bean.setUserProportion(new BigDecimal(bean.getPeopleNum() * 100.0 / totalUsers).setScale(1, BigDecimal.ROUND_HALF_UP));
                }
            }
        }
        return result;
    }

    @Override
    public String awardGiveOut(String memberId, BigDecimal amount, Integer type) {
        // 1、获取分布式锁防止重复调用 =====================================================
        String key = PayDistributePrefix.EXPERIENCE_SUBMIT + memberId;
        if (redisGlobalLock.lock(key)) {
            logger.info("----测试分布式锁-----memberId=" + memberId + ",----------amount=" + amount + "----type---=" + type + "--------时间=" + UF.getFormatDateTime(LocalDateTime.now()));
            try {
                MemberAccount memberAccount = memberAccountMapper.findByMemberId(memberId);
                Ensure.that(memberAccount == null).isTrue("17000421");
                if (SharePrizeTypeEnum.EXPERIENCE_GOLD.getType() == type) {
                    //更新账户--分布式锁
                    Map<String, Object> map = new HashMap<String, Object>();
                    map.put("id", memberAccount.getId());
                    map.put("totalGram", memberAccount.getTotalGram() + amount.intValue());
                    map.put("usableGram", memberAccount.getUsableGram() + amount.intValue());
                    memberAccountMapper.updateByPrimaryKeySelective(map);

                    //新增收支记录
                    ExperienceBillEntity experienceBillEntity = new ExperienceBillEntity();
                    experienceBillEntity.setMemberId(memberId);
                    experienceBillEntity.setTradeType(ExperienceTradeTypeEnum.DRAW.getType());
                    experienceBillEntity.setType(BillType.INCOME.getValue());
                    experienceBillEntity.setGram(amount.intValue());
                    experienceBillEntity.setOrderNo(SerialNumberWorker.getInstance().nextId());
                    experienceBillMapper.insert(experienceBillEntity);
                    return experienceBillEntity.getOrderNo();
                }

                if (SharePrizeTypeEnum.GOLD_BEAN.getType() == type) {
                    //更新账户--分布式锁
                    Map<String, Object> map = new HashMap<String, Object>();
                    map.put("id", memberAccount.getId());
                    map.put("totalBean", memberAccount.getTotalBean() + amount.intValue());
                    map.put("usableBean", memberAccount.getUsableBean() + amount.intValue());
                    int rows = memberAccountMapper.updateByPrimaryKeySelective(map);
                    Ensure.that(rows).isLt(1, "00000005");

                    PayGoldBeanRecord beanRecord = new PayGoldBeanRecord();
                    beanRecord.setOrderNO(OrderNOPrefixEnum.GB.name() + SerialNumberWorker.getInstance().nextId());
                    beanRecord.setType(1);
                    beanRecord.setRemark(GoldBeanRemarkEnum.SIGNIN_REWARDS.getRemark());
                    beanRecord.setGoldBean(amount.intValue());

                    beanRecord.setMemberId(memberAccount.getMemberId());
                    beanRecord.setAddTime(UF.getFormatDateTime(LocalDateTime.now()));
                    beanRecord.setSuccessTime(UF.getFormatDateTime(LocalDateTime.now()));
                    SerializeObject serializeObject = rateSettingsService.get(RateKeyEnum.GOLD_BEAN.getKey());
                    if (serializeObject != null && serializeObject.getData() != null) {
                        GoldBean goldBean = JSON.parseObject(JSONObject.toJSONString(serializeObject.getData()), GoldBean.class);
                        BigDecimal result = new BigDecimal(goldBean.getGoldGram()).divide(new BigDecimal(goldBean.getGoldBeanNum()));
                        beanRecord.setGoldGram(result.multiply(amount));
                    }

                    rows = payGoldBeanRecordMapper.insert(beanRecord);
                    Ensure.that(rows).isLt(1, "00000005");
                    return beanRecord.getOrderNO();
                }
            } catch (Exception e) {
                throw e;
            } finally {
                // 4、释放分布式锁 ================================================================
                redisGlobalLock.unlock(key);
            }
        } else {
            // 如果没有获取锁
            Ensure.that(true).isTrue("17000706");
        }
        return null;
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public String largeRecharge(String memberId, BigDecimal amount, Integer type, BigDecimal fee) {
        // 1、获取分布式锁防止重复调用 =====================================================
        String key = PayDistributePrefix.PAY_MEMBER_ACCOUNT + memberId;
        if (redisGlobalLock.lock(key)) {
            logger.info("----测试分布式锁-----memberId=" + memberId + ",----------amount=" + amount + "----type---=" + type + "--------时间=" + UF.getFormatDateTime(LocalDateTime.now()));
            try {
                MemberAccount memberAccount = memberAccountMapper.findByMemberId(memberId);
                Ensure.that(memberAccount == null).isTrue("17000421");
                BigDecimal actualAmount = amount.subtract(fee);
                //更新账户--分布式锁
                Map<String, Object> map = new HashMap<String, Object>();
                map.put("id", memberAccount.getId());
                map.put("fundAmount", memberAccount.getFundAmount().add(actualAmount));
                map.put("usableAmount", memberAccount.getUsableAmount().add(actualAmount));
                memberAccountMapper.updateByPrimaryKeySelective(map);

                //新增收支记录
                BillBean billBean = getBillBean(memberId, amount, fee);
                billBean.setType(BillType.INCOME.getValue());
                billBean.setTradeType(type);
                billService.save(billBean);

                //根据绑定银行卡获取银行卡相关信息
                BankCard bankCard = bankCardMapper.findDefaultByMemberId(memberId);
                Ensure.that(bankCard == null).isTrue("17000419");
                Trade trade = new Trade();
                trade.setId(UUIDUtils.base58Uuid());
                trade.setMemberId(memberId);
                trade.setTitle("大额转账充值");
                trade.setOrderId(billBean.getOrderId());
                trade.setThirdNumber("");
                trade.setAmount(amount);
                trade.setFee(fee);
                //已支付
                trade.setStatus(2);
                //交易类型 1:充值 2:买金 3:商城
                trade.setType(1);
                trade.setPayWay(1);
                trade.setBankCardId(bankCard.getId());
                trade.setPayAmount(BigDecimal.ZERO);
                trade.setSerialNumber(billBean.getOrderId());
                trade.setAddTime(LocalDateTime.now());
                trade.setModifyTime(LocalDateTime.now());
                tradeMapper.insert(trade);

                if (fee.compareTo(BigDecimal.ZERO) > 0) {
                    //新增收支记录
                    BillBean billBean2 = getBillBean(memberId, fee, BigDecimal.ZERO);
                    billBean2.setType(BillType.OUTLAY.getValue());
                    billBean2.setTradeType(TradeType.RECHARGE_FEE.getValue());
                    billService.save(billBean2);
                }

                //添加资金账户支出变动消息队列
                Msg msg = new Msg();
                msg.setMemberId(memberId);
                msg.setType(cn.ug.msg.bean.status.CommonConstants.MsgType.RECHARGE.getIndex());

                Map<String, String> paramMap = new HashMap<>();
                paramMap.put("date", UF.getFormatDate("MM月dd日", UF.getDateTime()));
                paramMap.put("amount", BigDecimalUtil.to2Point(actualAmount).toString());
                paramMap.put("usableAmount", BigDecimalUtil.to2Point(memberAccount.getUsableAmount().add(actualAmount)).toString());
                paramMap.put("whetherPush","NO");
                msg.setParamMap(paramMap);
                logger.info("-----消息-------"+paramMap.toString());
                amqpTemplate.convertAndSend(QUEUE_MSG_SEND, msg);

                //统计分析成功--充值
                MemberAnalyseMsgParamBean analyse = new MemberAnalyseMsgParamBean();
                analyse.setMemberId(trade.getMemberId());
                analyse.setType(cn.ug.analyse.bean.status.CommonConstants.MemberAnalyseType.RECHARGE.getIndex());
                amqpTemplate.convertAndSend(QUEUE_MEMBER_ANALYSE, analyse);

                //统计分析成功--交易
                MemberAnalyseMsgParamBean analyse1 = new MemberAnalyseMsgParamBean();
                analyse1.setMemberId(trade.getMemberId());
                analyse1.setType(cn.ug.analyse.bean.status.CommonConstants.MemberAnalyseType.TRADE.getIndex());
                amqpTemplate.convertAndSend(QUEUE_MEMBER_ANALYSE, analyse1);

                return billBean.getOrderId();
            } catch (Exception e) {
                throw e;
            } finally {
                // 4、释放分布式锁 ================================================================
                redisGlobalLock.unlock(key);
            }
        } else {
            // 如果没有获取锁
            Ensure.that(true).isTrue("17000706");
        }
        return null;
    }


    @Override
    public DataTable<MemberAccountManageBean> queryMemberAccountList(MemberAccountParamBean memberAccountParamBean) {
        com.github.pagehelper.Page<BankCardManageBean> pages = PageHelper.startPage(memberAccountParamBean.getPageNum(), memberAccountParamBean.getPageSize());
        List<MemberAccountManageBean> dataList = memberAccountMapper.queryMemberAccountList(memberAccountParamBean);
        return new DataTable<>(memberAccountParamBean.getPageNum(), memberAccountParamBean.getPageSize(), pages.getTotal(), dataList);
    }

    @Override
    public BigDecimal queryUsableAmount() {
        LoginBean loginBean = LoginHelper.getLoginBean();
        Ensure.that(loginBean == null).isTrue("00000102");
        MemberAccount memberAccount = memberAccountMapper.findByMemberId(loginBean.getId());
        if (memberAccount != null) {
            return memberAccount.getUsableAmount();
        } else {
            return BigDecimal.ZERO;
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public int withdraw(String memberId, BigDecimal amount) {
        // 1、获取分布式锁防止重复调用 =====================================================
        String key = PayDistributePrefix.PAY_MEMBER_ACCOUNT + memberId;
        if (redisGlobalLock.lock(key)) {
            try {
                MemberAccount memberAccount = memberAccountMapper.findByMemberId(memberId);
                Ensure.that(memberAccount == null).isTrue("17000501");
                Ensure.that(amount.compareTo(BigDecimal.valueOf(2)) < 0).isTrue("17000106");  //提现金额必须大于2元
                Ensure.that(memberAccount.getUsableAmount().compareTo(amount) < 0).isTrue("17000502");  //账户可用余额不足
                //更新账户--分布式锁
                Map<String, Object> map = new HashMap<String, Object>();
                map.put("id", memberAccount.getId());
                map.put("freezeAmount", memberAccount.getFreezeAmount().add(amount));
                map.put("usableAmount", memberAccount.getUsableAmount().subtract(amount));
                memberAccountMapper.updateByPrimaryKeySelective(map);
            } catch (Exception e) {
                throw e;
            } finally {
                // 4、释放分布式锁 ================================================================
                redisGlobalLock.unlock(key);
            }
        } else {
            // 如果没有获取锁
            Ensure.that(true).isTrue("17000706");
        }

        return 0;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean sellGold(String memberId, BigDecimal amount, BigDecimal fee, BigDecimal goldPrice, BigDecimal weight) {
        // 1、获取分布式锁防止重复调用 =====================================================
        String key = PayDistributePrefix.PAY_MEMBER_ACCOUNT + memberId;
        if (redisGlobalLock.lock(key)) {
            try {
                MemberAccount memberAccount = memberAccountMapper.findByMemberId(memberId);

                Ensure.that(memberAccount == null).isTrue("17000501");
                BigDecimal changeAmount = BigDecimalUtil.to2Point(amount.subtract(fee));      //卖金收益
                BigDecimal fundAmount = memberAccount.getFundAmount().add(changeAmount);      //总账户余额
                BigDecimal usableAmount = memberAccount.getUsableAmount().add(changeAmount);  //可用余额
                //更新账户--分布式锁
                Map<String, Object> map = new HashMap<String, Object>();
                map.put("id", memberAccount.getId());
                map.put("fundAmount", fundAmount);
                map.put("usableAmount", usableAmount);
                memberAccountMapper.updateByPrimaryKeySelective(map);

                BillBean billBean = getBillBean(memberId, amount, fee);
                billBean.setType(BillType.INCOME.getValue());
                billBean.setTradeType(TradeType.SELL_GOLD_INCOME.getValue());
                billBean.setGoldPrice(goldPrice);
                billService.save(billBean);
                if (fee.doubleValue() > 0) {
                    BillBean feeBillBean = getBillBean(memberId, fee, BigDecimal.ZERO);
                    feeBillBean.setType(BillType.OUTLAY.getValue());
                    feeBillBean.setTradeType(TradeType.SELL_GOLD_FEE.getValue());
                    feeBillBean.setGoldPrice(goldPrice);
                    billService.save(feeBillBean);
                }
                //添加资金账户收入变动消息队列
                payCommon.accountChangeMsgQueue(memberId, CommonConstants.MsgType.ACCOUNT_INCOME_CHANGE.getIndex(), changeAmount.toString(), fundAmount.toString());

                /**资金变动**/
                wxChangeSemd(memberId, usableAmount, changeAmount);
                /**卖出**/
                wxSaleSend(memberId, usableAmount, weight, changeAmount, goldPrice, fee);

            } catch (Exception e) {
                throw e;
            } finally {
                // 4、释放分布式锁 ================================================================
                redisGlobalLock.unlock(key);
            }
        } else {
            // 如果没有获取锁
            Ensure.that(true).isTrue("17000706");
        }
        return true;
    }

    @Override
    public boolean create(String memberId) {
        MemberAccount memberAccount = memberAccountMapper.findByMemberId(memberId);
        Ensure.that(memberAccount == null).isFalse("17000421");
        MemberAccount entity = new MemberAccount();
        entity.setId(UF.getRandomUUID());
        entity.setMemberId(memberId);
        //entity.setUsableGram(30);
        //entity.setTotalGram(30);
        memberAccountMapper.insert(entity);
        /*ExperienceBillEntity experienceBillEntity = new ExperienceBillEntity();
        experienceBillEntity.setGram(30);
        experienceBillEntity.setType(1);
        experienceBillEntity.setMemberId(memberId);
        experienceBillEntity.setOrderNo(OrderNOPrefixEnum.PE.name()+SerialNumberWorker.getInstance().nextId());
        experienceBillEntity.setTradeType(ExperienceTradeTypeEnum.REGISTER.getType());
        experienceBillEntity.setAddTime(UF.getDateTime());
        experienceBillEntity.setModifyTime(UF.getDateTime());
        experienceBillMapper.insert(experienceBillEntity);*/
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public String inviteReturnCash(String memberId, BigDecimal amount, Integer type) {
        // 1、获取分布式锁防止重复调用 =====================================================
        String key = PayDistributePrefix.PAY_MEMBER_ACCOUNT + memberId;
        if (redisGlobalLock.lock(key)) {
            logger.info("----测试分布式锁-----memberId=" + memberId + ",----------amount=" + amount + "----type---=" + type + "--------时间=" + UF.getFormatDateTime(LocalDateTime.now()));
            try {
                MemberAccount memberAccount = memberAccountMapper.findByMemberId(memberId);
                Ensure.that(memberAccount == null).isTrue("17000421");
                //更新账户--分布式锁
                Map<String, Object> map = new HashMap<String, Object>();
                map.put("id", memberAccount.getId());
                map.put("fundAmount", memberAccount.getFundAmount().add(amount));
                map.put("usableAmount", memberAccount.getUsableAmount().add(amount));
                if (TradeType.CASH_BACK.getValue() == type) {
                    map.put("couponAmount", memberAccount.getCouponAmount().add(amount));

                }
                if (TradeType.RATE_INCREASE.getValue() == type) {
                    map.put("interestAmount", memberAccount.getInterestAmount().add(amount));
                }
                memberAccountMapper.updateByPrimaryKeySelective(map);

                //新增收支记录
                BillBean billBean = getBillBean(memberId, amount, BigDecimal.ZERO);
                billBean.setType(BillType.INCOME.getValue());
                billBean.setTradeType(type);
                billService.save(billBean);

                if (TradeType.CASH_REWARDS.getValue() == type) {
                    /**发送微信推送**/
                    wxSend(memberId, amount, type, memberAccount.getUsableAmount().add(amount));
                }
                return billBean.getOrderId();
            } catch (Exception e) {
                throw e;
            } finally {
                // 4、释放分布式锁 ================================================================
                redisGlobalLock.unlock(key);
            }
        } else {
            // 如果没有获取锁
            Ensure.that(true).isTrue("17000706");
        }
        return null;
    }

    @Override
    public void batchUpdate(Integer type) {
        List<MemberAccount> memberAccountList = memberAccountMapper.queryIds();
        if (!CollectionUtils.isEmpty(memberAccountList)) {
            List<String> ids = memberAccountList.stream().map(o -> o.getId()).collect(Collectors.toList());
            //扣减一半
            if (type == 1) {
                memberAccountMapper.updateGoldBeanHalf(ids);
            } else {
                //扣减全部
                memberAccountMapper.updateGoldBeanAll(ids);
            }
            BigDecimal cardinal = new BigDecimal(2);
            int rows = 0;
            for (MemberAccount memberAccount : memberAccountList) {
                BigDecimal resultNum;
                if (type == 1) {
                    resultNum = new BigDecimal(memberAccount.getUsableBean()).divide(cardinal).setScale(0, BigDecimal.ROUND_HALF_UP);
                } else {
                    resultNum = new BigDecimal(memberAccount.getUsableBean());
                }
                PayGoldBeanRecord beanRecord = new PayGoldBeanRecord();
                beanRecord.setOrderNO(OrderNOPrefixEnum.GB.name() + SerialNumberWorker.getInstance().nextId());
                beanRecord.setType(2);
                beanRecord.setRemark(GoldBeanRemarkEnum.GOLD_BEAN_DEDUCT.getRemark());
                beanRecord.setGoldBean(resultNum.intValue());

                beanRecord.setMemberId(memberAccount.getMemberId());
                beanRecord.setAddTime(UF.getFormatDateTime(LocalDateTime.now()));
                beanRecord.setSuccessTime(UF.getFormatDateTime(LocalDateTime.now()));
                SerializeObject serializeObject = rateSettingsService.get(RateKeyEnum.GOLD_BEAN.getKey());
                if (serializeObject != null && serializeObject.getData() != null) {
                    GoldBean goldBean = JSON.parseObject(JSONObject.toJSONString(serializeObject.getData()), GoldBean.class);
                    BigDecimal result = new BigDecimal(goldBean.getGoldGram()).divide(new BigDecimal(goldBean.getGoldBeanNum()));
                    beanRecord.setGoldGram(result.multiply(resultNum));
                }

                rows = payGoldBeanRecordMapper.insert(beanRecord);
                Ensure.that(rows).isLt(1, "00000005");
            }
        }
    }


    @Override
    public void expireSmsSend(Integer type) {
        List<MemberAccount> memberAccountList = memberAccountMapper.queryIds();
        if (!CollectionUtils.isEmpty(memberAccountList)) {
            //扣减一半
            if (type == 1) {
                BigDecimal cardinal = new BigDecimal(2);
                for (MemberAccount memberAccount : memberAccountList) {
                    Msg msg = new Msg();
                    msg.setMemberId(memberAccount.getMemberId());
                    msg.setType(CommonConstants.MsgType.EXPERIENCE_GOLDBEAN_DUE.getIndex());
                    Map<String, String> paramMap = new HashMap<>();
                    paramMap.put("content", String.valueOf(new BigDecimal(memberAccount.getUsableBean()).divide(cardinal).setScale(0, BigDecimal.ROUND_HALF_UP)));
                    msg.setParamMap(paramMap);

                    // 凌晨一点触发任务，延后八个小时发送短信
                    amqpTemplate.convertAndSend(QUEUE_DELAY_PER_MESSAGE_TTL_MSG_SEND, msg, new DelayMessagePostProcessor(8 * 60 * 60 * 1000));
                }
            } else {
                //扣减全部
                for (MemberAccount memberAccount : memberAccountList) {
                    Msg msg = new Msg();
                    msg.setMemberId(memberAccount.getMemberId());
                    msg.setType(CommonConstants.MsgType.EXPERIENCE_GOLDBEAN_DUE.getIndex());
                    Map<String, String> paramMap = new HashMap<>();
                    paramMap.put("content", String.valueOf(memberAccount.getUsableBean()));
                    msg.setParamMap(paramMap);
                    // 凌晨一点触发任务，延后八个小时发送短信
                    amqpTemplate.convertAndSend(QUEUE_DELAY_PER_MESSAGE_TTL_MSG_SEND, msg, new DelayMessagePostProcessor(8 * 60 * 60 * 1000));
                }
            }
        }
    }

    private void wxSend(String memberId, BigDecimal amount, Integer type, BigDecimal usableAmount) {
        /**
         * 微信服务号消息推送  收支变动
         */
        WxMessageParamBean wxMessageParamBean = new WxMessageParamBean();

        wxMessageParamBean.setMemberId(memberId);
        wxMessageParamBean.setType(WxTemplateEnum.FUND_CHANGE.getType());
        WxTemplateEnum wxTemplateEnum = WxTemplateEnum.getWxTemplateByCode(WxTemplateEnum.FUND_CHANGE.getType());
        WxNotifyData wxNotifyData = new WxNotifyData();

        Map<String, WxNotifyData.TemplateDataAttr> wxParamMap = new HashMap();
        WxNotifyData.TemplateDataAttr first = new WxNotifyData.TemplateDataAttr();
        first.setDataValue(wxTemplateEnum.getFirstData());
        wxParamMap.put("first", first);

        WxNotifyData.TemplateDataAttr keyword1 = new WxNotifyData.TemplateDataAttr();
        keyword1.setDataValue(TradeType.getRemark(type));
        wxParamMap.put("keyword1", keyword1);

        WxNotifyData.TemplateDataAttr keyword2 = new WxNotifyData.TemplateDataAttr();
        keyword2.setDataValue(BigDecimalUtil.to2Point(amount).toString());
        wxParamMap.put("keyword2", keyword2);

        WxNotifyData.TemplateDataAttr keyword3 = new WxNotifyData.TemplateDataAttr();
        keyword3.setDataValue(DateUtils.dateToStr(new Date(), DateUtils.patter));
        wxParamMap.put("keyword3", keyword3);

        WxNotifyData.TemplateDataAttr keyword4 = new WxNotifyData.TemplateDataAttr();
        keyword4.setDataValue(BigDecimalUtil.to2Point(usableAmount).toString());
        wxParamMap.put("keyword4", keyword4);

        WxNotifyData.TemplateDataAttr remark = new WxNotifyData.TemplateDataAttr();
        remark.setDataValue(wxTemplateEnum.getRemark());
        wxParamMap.put("remark", remark);

        wxNotifyData.setData(wxParamMap);
        wxMessageParamBean.setTemplateData(wxNotifyData);
        amqpTemplate.convertAndSend(QUEUE_MSG_WX_SEND, wxMessageParamBean);
    }

    private void wxSaleSend(String memberId, BigDecimal usableAmount, BigDecimal reduceGram, BigDecimal changeAmount, BigDecimal currentGoldPrice, BigDecimal fee) {
        MemberGoldBean memberGoldBean = memberGoldService.findByMemberId(memberId);
        /**
         * 微信服务号消息推送  卖出
         */
        WxMessageParamBean wxMessageParamBean = new WxMessageParamBean();
        wxMessageParamBean.setMemberId(memberId);
        //消息推送类型
        wxMessageParamBean.setType(WxTemplateEnum.SALE_GOLD_SUCCEED.getType());
        WxTemplateEnum wxTemplateEnum = WxTemplateEnum.getWxTemplateByCode(WxTemplateEnum.SALE_GOLD_SUCCEED.getType());
        WxNotifyData wxNotifyData = new WxNotifyData();

        Map<String, WxNotifyData.TemplateDataAttr> wxParamMap = new HashMap();
        WxNotifyData.TemplateDataAttr first = new WxNotifyData.TemplateDataAttr();
        first.setDataValue(wxTemplateEnum.getFirstData().replace("{weight}", BigDecimalUtil.to5Point(reduceGram).toString()).
                replace("{balance}", BigDecimalUtil.to5Point(memberGoldBean.getUsableAmount()).toString()).replace("{money}", BigDecimalUtil.to2Point(changeAmount).toString()));
        wxParamMap.put("first", first);

        WxNotifyData.TemplateDataAttr keyword1 = new WxNotifyData.TemplateDataAttr();
        keyword1.setDataValue(BigDecimalUtil.to2Point(changeAmount).toString());
        wxParamMap.put("keyword1", keyword1);

        WxNotifyData.TemplateDataAttr keyword2 = new WxNotifyData.TemplateDataAttr();
        keyword2.setDataValue(BigDecimalUtil.to5Point(reduceGram).toString());
        wxParamMap.put("keyword2", keyword2);

        WxNotifyData.TemplateDataAttr keyword3 = new WxNotifyData.TemplateDataAttr();
        keyword3.setDataValue(BigDecimalUtil.to2Point(currentGoldPrice).toString());
        wxParamMap.put("keyword3", keyword3);

        WxNotifyData.TemplateDataAttr keyword4 = new WxNotifyData.TemplateDataAttr();
        keyword4.setDataValue(BigDecimalUtil.to2Point(fee).toString());
        wxParamMap.put("keyword4", keyword4);

        WxNotifyData.TemplateDataAttr keyword5 = new WxNotifyData.TemplateDataAttr();
        keyword5.setDataValue(DateUtils.dateToStr(new Date(), DateUtils.patter));
        wxParamMap.put("keyword5", keyword5);

        WxNotifyData.TemplateDataAttr remark = new WxNotifyData.TemplateDataAttr();
        remark.setDataValue(wxTemplateEnum.getRemark());
        wxParamMap.put("remark", remark);

        wxNotifyData.setData(wxParamMap);
        wxMessageParamBean.setTemplateData(wxNotifyData);
        amqpTemplate.convertAndSend(QUEUE_MSG_WX_SEND, wxMessageParamBean);
    }

    public void wxChangeSemd(String memberId, BigDecimal usableAmount, BigDecimal changeAmount) {
        /**
         * 微信服务号消息推送  资金变动
         */
        WxMessageParamBean wxMessageParamBean = new WxMessageParamBean();
        wxMessageParamBean.setMemberId(memberId);
        //消息推送类型
        wxMessageParamBean.setType(WxTemplateEnum.FUND_CHANGE.getType());
        WxTemplateEnum wxTemplateEnum = WxTemplateEnum.getWxTemplateByCode(WxTemplateEnum.FUND_CHANGE.getType());
        WxNotifyData wxNotifyData = new WxNotifyData();

        Map<String, WxNotifyData.TemplateDataAttr> wxParamMap = new HashMap();
        WxNotifyData.TemplateDataAttr first = new WxNotifyData.TemplateDataAttr();
        first.setDataValue(wxTemplateEnum.getFirstData());
        wxParamMap.put("first", first);

        WxNotifyData.TemplateDataAttr keyword1 = new WxNotifyData.TemplateDataAttr();
        keyword1.setDataValue(TradeType.SELL_GOLD_INCOME.getRemark());
        wxParamMap.put("keyword1", keyword1);

        WxNotifyData.TemplateDataAttr keyword2 = new WxNotifyData.TemplateDataAttr();
        keyword2.setDataValue(BigDecimalUtil.to2Point(changeAmount).toString());
        wxParamMap.put("keyword2", keyword2);

        WxNotifyData.TemplateDataAttr keyword3 = new WxNotifyData.TemplateDataAttr();
        keyword3.setDataValue(DateUtils.dateToStr(new Date(), DateUtils.patter));
        wxParamMap.put("keyword3", keyword3);

        WxNotifyData.TemplateDataAttr keyword4 = new WxNotifyData.TemplateDataAttr();
        keyword4.setDataValue(BigDecimalUtil.to2Point(usableAmount).toString());
        wxParamMap.put("keyword4", keyword4);

        WxNotifyData.TemplateDataAttr remark = new WxNotifyData.TemplateDataAttr();
        remark.setDataValue(wxTemplateEnum.getRemark());
        wxParamMap.put("remark", remark);

        wxNotifyData.setData(wxParamMap);
        wxMessageParamBean.setTemplateData(wxNotifyData);
        amqpTemplate.convertAndSend(QUEUE_MSG_WX_SEND, wxMessageParamBean);
    }

    @Override
    public MemberAccountBean findByMemberId(String memberId) {
        Ensure.that(memberId).isNull("17000311");
        MemberAccount memberAccount = memberAccountMapper.findByMemberId(memberId);
        Ensure.that(memberAccount == null).isTrue("17000501");
        MemberAccountBean memberAccountBean = dozerBeanMapper.map(memberAccount, MemberAccountBean.class);
        return memberAccountBean;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void orderExpireUnfreezeAmount(String orderId) {
        Ensure.that(orderId).isNull("");
        Trade trade = tradeMapper.queryTradeByOrderId(orderId);
        if (trade != null) {
            //处理待支付和失败订单
            if (cn.ug.pay.bean.status.CommonConstants.PayStatus.YESPAY.getIndex() != trade.getStatus()) {
                if (trade.getAmount().compareTo(trade.getPayAmount()) != 0) {
                    //计算出冻结金额
                    BigDecimal freezeAmount = trade.getAmount().subtract(trade.getPayAmount());
                    //进行账户资金解冻
                    // 1、获取分布式锁防止重复调用 =====================================================
                    String key = PayDistributePrefix.PAY_MEMBER_ACCOUNT + trade.getMemberId();
                    if (redisGlobalLock.lock(key)) {
                        try {
                            MemberAccount memberAccount = memberAccountMapper.findByMemberId(trade.getMemberId());
                            Ensure.that(memberAccount == null).isTrue("17000421");
                            //更新账户--分布式锁
                            Map<String, Object> map = new HashMap<String, Object>();
                            map.put("id", memberAccount.getId());
                            map.put("freezeAmount", memberAccount.getFreezeAmount().subtract(freezeAmount));
                            map.put("usableAmount", memberAccount.getUsableAmount().add(freezeAmount));
                            memberAccountMapper.updateByPrimaryKeySelective(map);
                        } catch (Exception e) {
                            throw e;
                        } finally {
                            // 4、释放分布式锁 ================================================================
                            redisGlobalLock.unlock(key);
                        }
                    } else {
                        // 如果没有获取锁
                        Ensure.that(true).isTrue("17000706");
                    }
                }
            }
        }
    }

    @Override
    public void withdrawFail(String memberId, BigDecimal freezeAmount, BigDecimal amount) {
        MemberAccount memberAccount = memberAccountMapper.findByMemberId(memberId);
        if (memberAccount != null) {
            Map<String, Object> paramMap = new HashMap<String, Object>();
            BigDecimal freezeToatalAmount = memberAccount.getFreezeAmount().subtract(freezeAmount);
            BigDecimal usableTotalAmount = memberAccount.getUsableAmount().add(amount);
            paramMap.put("id", memberAccount.getId());
            paramMap.put("freezeAmount", freezeToatalAmount.toString());
            paramMap.put("usableAmount", usableTotalAmount.toString());
            paramMap.put("fundAmount", usableTotalAmount.add(freezeToatalAmount).toString());
            int rows = memberAccountMapper.updateByPrimaryKeySelective(paramMap);
            Ensure.that(rows).isLt(1, "00000005");
        }
        logger.info("---账户余额更新完成（提现失败）---" + memberId + ",freezeAmount：" + freezeAmount + ",amount:" + amount);
    }

    @Override
    public void withdrawSuccess(String memberId, BigDecimal amount) {
        MemberAccount memberAccount = memberAccountMapper.findByMemberId(memberId);
        if (memberAccount != null) {
            Map<String, Object> paramMap = new HashMap<String, Object>();
            paramMap.put("id", memberAccount.getId());
            paramMap.put("fundAmount", memberAccount.getFundAmount().subtract(amount).toString());
            paramMap.put("freezeAmount", memberAccount.getFreezeAmount().subtract(amount).toString());
            memberAccountMapper.updateByPrimaryKeySelective(paramMap);
        }
        logger.info("---账户余额更新完成（提现成功）---" + memberId);
    }

    /**
     * 返回收支明细
     *
     * @param memberId 会员ID
     * @param amount   总金额
     * @param fee      手续费
     * @return
     */
    public static BillBean getBillBean(String memberId, BigDecimal amount, BigDecimal fee) {
        BillBean billBean = new BillBean();
        billBean.setOrderId(SerialNumberWorker.getInstance().nextId());
        billBean.setMemberId(memberId);
        billBean.setAmount(amount);
        billBean.setFee(fee);
        billBean.setActualAmount(amount.subtract(fee));
        return billBean;
    }
}
